package org.pwwong.scalautils.animation

import java.awt.{Color,BasicStroke}

import org.pwwong.fxscalautils.components.layouthints._
import org.pwwong.fxscalautils.Predef._

import org.pwwong.scalautils.signals.{BindableSignal,Signal}
import org.pwwong.scalautils.time._

object Predef {
  
  // Interpolators
  // 	(completionFraction) => actualFraction
  object Interpolators {
    def Linear(in:Double) = in  
    //case class AccelDeacell(val accelWidth:Double, val deaccelWidth:Double) extends (Double=>Double) {
    // OMG De Boor's Algorithm... OOOOOooooo nooees
    //}
  }
  
  // Evaluators 
  // 	(completionFraction, start, end) => cur
  implicit object IntEval extends ((Double,Int,Int)=>Int){
    override def apply(frac:Double,start:Int,end:Int) = start+((end-start)*frac).toInt
  }
  implicit object DoubleEval extends ((Double,Double,Double)=>Double){
    override def apply(frac:Double,start:Double,end:Double):Double = start+((end-start)*frac)
  }
  implicit object ColorEval extends ((Double,Color,Color)=>Color){
    override def apply(frac:Double,start:Color,end:Color) = new Color( start.getRed   + ((end.getRed.toDouble  - start.getRed.toDouble)  *frac).toInt,
                                                                       start.getGreen + ((end.getGreen.toDouble - start.getGreen.toDouble)*frac).toInt,
                                                                       start.getBlue   + ((end.getBlue.toDouble  - start.getBlue.toDouble) *frac).toInt  )
  }
  implicit object BasicStrokeEval extends ((Double,BasicStroke,BasicStroke)=>BasicStroke){
    override def apply(frac:Double,start:BasicStroke,end:BasicStroke) = new BasicStroke(start.getLineWidth().toFloat + (frac.toFloat*(end.getLineWidth()-start.getLineWidth()))) 
  }
  
  // Handle Size
  implicit object PXEval extends ((Double,PX,PX)=>PX){
    override def apply(frac:Double,start:PX,end:PX):PX = PX(start.value + ((end.value - start.value)*frac).toInt)
  }
  implicit object PCTEval extends ((Double,PCT,PCT)=>PCT){
    override def apply(frac:Double,start:PCT,end:PCT):PCT = PCT(start.value + ((end.value - start.value)*frac))
  }
  
  implicit object SizeValueEvaluator extends ((Double,LayoutValue with Product,LayoutValue with Product)=>LayoutValue with Product) {
    override def apply(frac:Double,start:LayoutValue with Product,end:LayoutValue with Product):LayoutValue with Product = null
  }
  
  // Handle Position
  implicit object XPositionEval extends ((Double,XPosition[LayoutValue with Product],XPosition[LayoutValue with Product])=>XPosition[LayoutValue with Product]) {
    override def apply(frac:Double,start:XPosition[LayoutValue with Product],end:XPosition[LayoutValue with Product]):XPosition[LayoutValue with Product] = null
  }
  implicit object YPositionEval extends ((Double,YPosition[LayoutValue with Product],YPosition[LayoutValue with Product])=>YPosition[LayoutValue with Product]) {
    override def apply(frac:Double,start:YPosition[LayoutValue with Product],end:YPosition[LayoutValue with Product]):YPosition[LayoutValue with Product] = null
  }
  implicit object YPositionPXEval extends ((Double,YPosition[PX],YPosition[PX])=>YPosition[PX]){
    override def apply(frac:Double,start:YPosition[PX],end:YPosition[PX]):YPosition[PX] = {
      YPosition[PX]( end.origin, PXEval(frac,start.offset,end.offset) )
    }
  }
  implicit object YPositionPCTEval extends ((Double,YPosition[PCT],YPosition[PCT])=>YPosition[PCT]){
    override def apply(frac:Double,start:YPosition[PCT],end:YPosition[PCT]):YPosition[PCT] = {
      YPosition[PCT]( end.origin, PCTEval(frac,start.offset,end.offset) )
    }
  }
  
  implicit object XPositionPXEval extends ((Double,XPosition[PX],XPosition[PX])=>XPosition[PX]){
    override def apply(frac:Double,start:XPosition[PX],end:XPosition[PX]):XPosition[PX] = {
      XPosition[PX]( end.origin, PXEval(frac,start.offset,end.offset) )
    }
  }
  implicit object XPositionPCTEval extends ((Double,XPosition[PCT],XPosition[PCT])=>XPosition[PCT]){
    override def apply(frac:Double,start:XPosition[PCT],end:XPosition[PCT]):XPosition[PCT] = {
      XPosition[PCT]( end.origin, PCTEval(frac,start.offset,end.offset) )
    }
  }
  
  // RepeatBehavior
  class NumberedRepeatBehaviorBuilder(val num:Int){
    def repeats:RepeatBehavior = NumberedRepeats(num,false)
    def cycles:RepeatBehavior = NumberedRepeats(num,true)
  }
  
  implicit def intToNumberedRepeatBehaviorBuilder(num:Int):NumberedRepeatBehaviorBuilder = new NumberedRepeatBehaviorBuilder(num)
  object infinite extends NumberedRepeatBehaviorBuilder(0){
    override def repeats:RepeatBehavior = RepeatIndefinitely(false)
    override def cycles:RepeatBehavior = RepeatIndefinitely(true)
  }
  
  object OneTimeRepeatBehavior extends NumberedRepeats(1,false)
  
  // Duration
  implicit def longToDuration(value:Long):Duration = Seconds(value)
  implicit def intToDuration(value:Int):Duration = Seconds(value)  
    
  // Builds an endpoint Animation
  def ->[T](end:T)(implicit eval:(Double,T,T)=>T):EndAnimation[T] = new EndAnimation[T](end)(eval)
  
  // Builds an offset Animation
  def +>[T](offset:T)(implicit eval:(Double,T,T)=>T):OffsetAnimation[T] = new OffsetAnimation[T](offset)(eval)
  
  // Implicitly builds an AnimatedSignal from an Animation}
  implicit def builderToStartEndAnimation[T](builder:StartEndAnimation[T]):Signal[T] = {
    require(builder.duration != None, "StartEndAnimation must have a duration specified")
    val rtn = new AnimatedSignal[T](
      builder.startValue, 
      builder.endValue, 
      builder.repeatBehavior, 
      builder.duration.get,
      Milliseconds(1000/builder.framesPerSec),
      builder.evaluator,
      builder.interpolator
    )
    rtn.start
    rtn
  }
  /*
  implicit def builderToSizeValue[T<:SizeValue](builder:StartEndAnimation[T]):Signal[T] = {
    println("builderToSizeValue")
    require(builder.duration != None, "StartEndAnimation must have a duration specified")
    val rtn = new AnimatedSignal[T](
      builder.startValue, 
      builder.endValue, 
      builder.repeatBehavior, 
      builder.duration.get,
      Milliseconds(1000/builder.framesPerSec),
      builder.evaluator,
      builder.interpolator
    )
    rtn.start
    rtn
  }
  
  implicit def builderToYPosition[T<:YPosition](builder:StartEndAnimation[T]):Signal[T] = {
    println("builderToYPosition")
    require(builder.duration != None, "StartEndAnimation must have a duration specified")
    val rtn = new AnimatedSignal[T](
      builder.startValue, 
      builder.endValue, 
      builder.repeatBehavior, 
      builder.duration.get,
      Milliseconds(1000/builder.framesPerSec),
      builder.evaluator,
      builder.interpolator
    )
    rtn.start
    rtn
  }
  
  implicit def builderToXPosition[T<:XPosition](builder:StartEndAnimation[T]):Signal[T] = {
    println("builderToXPosition")
    require(builder.duration != None, "StartEndAnimation must have a duration specified")
    val rtn = new AnimatedSignal[T](
      builder.startValue, 
      builder.endValue, 
      builder.repeatBehavior, 
      builder.duration.get,
      Milliseconds(1000/builder.framesPerSec),
      builder.evaluator,
      builder.interpolator
    )
    rtn.start
    rtn
  }*/
  
  implicit def twoTupleToAnimation[T](startEnd:(T,T))(implicit evaluator:(Double,T,T)=>T):StartEndAnimation[T] = {
    new StartEndAnimation[T](startEnd._1, startEnd._2)(evaluator)
  }
}
